#include <avr/io.h>
#include <string.h>
#include "key.h"
#include "global.h"
#include "OLED.h"
#include "spi.h"

#define OLED_ACTIVE() 		clrbit(PORT_OLED_OUT, PIN_OLED_CS)
#define OLED_DEACTIVE()   setbit(PORT_OLED_OUT, PIN_OLED_CS)

#define CMD_MODE()			clrbit(PORT_OLED_OUT,PIN_OLED_DC)
#define DATA_MODE()		setbit(PORT_OLED_OUT,PIN_OLED_DC)

#define FONT_8X16_BASE_H		0x0F
#define FONT_8X16_BASE_L		0xCF00

#define FONT_HZ_BASE0			0x0C0000
#define FONT_HZ_BASE1			(FONT_HZ_BASE0 + 0x69C0)

typedef union frame_buffer{
	union{
		uint8_t fb0[1024];
		uint8_t fb1[8][128];
	};
} frame_buffer_t;

//=========================================
//uint8_t     g_display_buffer[1024];
frame_buffer_t g_frame_buffer;

uint8_t     g_cursor_X, g_cursor_Y;

const uint8_t oled_cfg_list[] = {
//	0x00,		/*set lower column address*/
//	0x10,		/*set higher column address*/
	0x20,      /*Horizontal addressing mode*/
//	0x40,		/*set display start line*/
	0xB0,		/*set page address*/
	0x81,0x7f,	/*contract control  128   ??? FIXME*/
//	0xA0,		/*set segment remap*/
//	0xA6,		/*normal / reverse*/
//	0xA8,0x3F,	/*multiplex ratio duty = 1/64*/
//	0xC0,		/*Com scan direction*/
//	0xD3,0x00,	/*set display offset*/
//	0xD5,0x80,	/*set osc division*/
	0xD9,0xf1,	/*set pre-charge period*/
//	0xDA,0x12,	/*set COM pins*/
//	0xdb,0x40,	/*set vcomh*/
	0x8d,0x14,	/*set charge pump enable*/
	0xAF,		/*display ON*/
};
void OLED_print_hz(uint16_t hz);

void OLED_Write (uint8_t cmd) 
{
    USARTD0.DATA= cmd;
    while(!testbit(USARTD0.STATUS,6));
    setbit(USARTD0.STATUS,6);
}

void OLED_setBrightness(uint8_t brightness)
{
	OLED_ACTIVE();
	CMD_MODE();
	OLED_Write (0x81);
	OLED_Write (brightness);
	OLED_DEACTIVE();
}

void OLED_Init(void)	
{
#if 0 //inited by bootloader
	uint8_t i;

	// Initialize USARTD0 for OLED
	USARTD0.BAUDCTRLA = 0x08;	// BSEL=1 (SPI clock = 8MHz, SSD1306 max is 10MHz)
	USARTD0.CTRLC	  = 0xC0;	// Master SPI mode,
	USARTD0.CTRLB	  = 0x08;	// Enable TX

    // Power up sequence
    clrbit(PORT_OLED_OUT, PIN_OLED_RST);         // Reset Low for 30 uS
  	short_delay();
    setbit(PORT_OLED_OUT, PIN_OLED_RST);         // Reset Low for 30 uS
    short_delay();

	OLED_ACTIVE();
	CMD_MODE();
	for(i=0; i<sizeof(oled_cfg_list); i++){
		OLED_Write(oled_cfg_list[i]);
	}
	OLED_DEACTIVE();
#endif
	memset(g_frame_buffer.fb0, 0, 1024);
}

// Transfer display buffer to LCD
void OLED_Reflash(void) 
{
    uint16_t i;

    OLED_ACTIVE();
	CMD_MODE();
    OLED_Write(OLED_SET_PAGE);
    OLED_Write(OLED_SET_COL_HI);    // Set column at 0
    OLED_Write(OLED_SET_COL_LO);

    DATA_MODE();
   	// Send data to display
    for(i=0; i<1024; i++) {
        while(!testbit(USARTD0.STATUS,5));  // Wait for DATA empty
		USARTD0.DATA= g_frame_buffer.fb0[i];
		g_frame_buffer.fb0[i] = 0;//clear
    }
    while(!testbit(USARTD0.STATUS,6));      // Wait until transmit done
    setbit(USARTD0.STATUS,6);               // Clear flag
    OLED_DEACTIVE();
	g_cursor_X = 0;
	g_cursor_Y = 0;
}
/*
void OLED_clr_lines(uint8_t line_mask)
{
	uint8_t i,j, *p;

	for(i=0; i<8; i++) {
		if(line_mask & 1) {
			p = g_frame_buffer.fb1[i];
			for(j=0; j<128; j++) {
				*p++ = 0;
			}
		}
		line_mask >>= 1;
	}
	OLED_Reflash();

}

void OLED_clr_display(void) 
{
    OLED_clr_lines(0xff);
	g_cursor_X = 0;
	g_cursor_Y = 0;
}
*/
/*
void OLED_write_fb(uint8_t data)
{
    g_display_buffer[((uint16_t)(g_cursor_Y<<7)) + (g_cursor_X++)] = data;
}
*/

void OLED_print_char(char ch) 
{
	uint16_t offset;
	uint8_t *pointer;

	if(ch == '\n') {
		g_cursor_X = 0;
		g_cursor_Y += 2;
	} else if( ch >= ' ' ){
		offset = FONT_8X16_BASE_L + ((uint16_t)(ch - ' ') << 4);
		pointer = g_frame_buffer.fb1[g_cursor_Y] + g_cursor_X;

		spi_read_data(FONT_8X16_BASE_H, offset, pointer, 8);
		spi_read_data(FONT_8X16_BASE_H, offset+8, pointer+128, 8);
		if(g_cursor_X >= 120) {
			g_cursor_Y += 2;
			g_cursor_X = 0;
		} else {
			g_cursor_X += 8;
		}
	}
}

void OLED_print_hz(uint16_t hz)
{
	uint32_t fontaddr;
	uint8_t *pointer;
	uint32_t H = (hz>>8)&0xff;
	uint32_t L = hz&0xff;
	uint16_t offset_l;
	uint8_t offset_H;

	if(g_cursor_X >= (128-8)) {
		g_cursor_Y += 2;
		g_cursor_X = 0;
	}
	
	if(H < 0xB0) {
		fontaddr = ((H - 0xA1)*94 + (L-0xA1))*32 + FONT_HZ_BASE0;
	} else {
		fontaddr = ((H - 0xB0)*94 + (L-0xA1))*32 + FONT_HZ_BASE1;
	}

	offset_l = fontaddr & 0xffff;
	offset_H = (fontaddr >> 16) & 0xff;
	pointer = g_frame_buffer.fb1[g_cursor_Y] + g_cursor_X;

	spi_read_data(offset_H, offset_l, pointer, 16);
	spi_read_data(offset_H, offset_l+16, pointer+128, 16);

	g_cursor_X += 16;
	if(g_cursor_X >= 128) {
		g_cursor_Y += 2;
		g_cursor_X = 0;
	}
}

bool OLED_is_offscreen(void)
{
	return g_cursor_Y >= 8;
}

bool OLED_almost_offscreen(void)
{
	return (g_cursor_Y == 6) && (g_cursor_X >= (128 - 8));
}

void OLED_print_string (const char *ptr) 
{
    uint8_t c;
    while ((c=*ptr++) != 0x00) {
		if(c<0x7F) {
			OLED_print_char(c);
		} else if(c != 0xFF){
			OLED_print_hz((((uint8_t)c) << 8) + ((uint8_t)(*ptr++)));
		}
    }
	//OLED_Reflash();
}

